/*
 *  Copyright (C) 2010 Glauco
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.swing.components.util;

import java.awt.Component;
import java.awt.Container;
import java.awt.FocusTraversalPolicy;
import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Glauco
 */
public class FocusManager implements PropertyChangeListener{

    /**
     * Container ativo, que está sendo mostrado na tela
     */
    Container activeContainer;

    /**
     * Component anterior na troca do Focus
     */
    Component oldComponent;

    /**
     * Component onde o Focus foi parar
     */
    Component newComponent;

    /**
     * FocusTraversalPolicy do activeContainer
     */
    FocusTraversalPolicy ftp;

    /**
     * Ultimo comonent que pode receber o Focus do activeContainer
     */
    Component lastComponet;

    /**
     * Lista de FocusTraversalListener adicionados que serão processados.
     */
    List<FocusTraversalListener> focusTraversalList;

    /**
     * Lista de FocusTraversal do container ativo.
     */
    List<FocusTraversal> activeFocusTraversalList;

    @SuppressWarnings("LeakingThisInConstructor")
    private FocusManager() {
        focusTraversalList = new ArrayList<FocusTraversalListener>();
        activeFocusTraversalList = new ArrayList<FocusTraversal>();
        KeyboardFocusManager focusManager =
                KeyboardFocusManager.getCurrentKeyboardFocusManager();
        focusManager.addPropertyChangeListener(this);
    }

    public static FocusManager getInstance() {
        return FocusManagerHolder.INSTANCE;
    }

    public void addFocusTraversal(FocusTraversalListener focusTraversal){
        if(!focusTraversalList.contains(focusTraversal))
            focusTraversalList.add(focusTraversal);
    }

    public void removeFocusTraversal(FocusTraversalListener focusTraversal){
        focusTraversalList.remove(focusTraversal);
    }

    public void propertyChange(PropertyChangeEvent evt) {
        if(evt.getPropertyName().equals("focusOwner")){

            Component o = null;
            Component n = null;

            if(evt.getOldValue() != null &&
                    evt.getOldValue() instanceof Component)
                o = (Component) evt.getOldValue();
            if(evt.getNewValue() != null &&
                    evt.getNewValue() instanceof Component)
                n = (Component) evt.getNewValue();

            processFocusOwner(o, n);
        }
        if(evt.getPropertyName().equals("currentFocusCycleRoot")){
            if (evt.getNewValue() != null){
                Object o = evt.getNewValue();
                if(o instanceof Container)
                    processNewFocusCycleRoot((Container) o);
            }
        }
    }

    private void processNewFocusCycleRoot(Container container){
        activeContainer = container;
        ftp = activeContainer.getFocusTraversalPolicy();
        if(ftp != null)
            lastComponet = ftp.getLastComponent(activeContainer);
        else
            lastComponet = null;
    }

    private void processFocusOwner(Component oldValue, Component newValue){
        if (oldComponent == null)
            oldComponent = oldValue;
        if (newValue != null && !newValue.equals(oldComponent))
            newComponent = newValue;

        if(newComponent != null & oldComponent != null){
//            checkFocusTraversal();
            for (FocusTraversalListener ft : focusTraversalList) {
                ft.checkFocusTraversal(activeContainer, ftp,
                        oldComponent, newComponent, lastComponet);
            }
            oldComponent = null;
            newComponent = null;
        }
    }

//    private void checkFocusTraversal() {
//        Component actualComponent = oldComponent;
//
//        // retorna se os componentes forem de janelas diferentes
//        // ou o container ativo for nulo ou não ter focusTravelPolice
//        if(oldComponent.getFocusCycleRootAncestor() !=
//                newComponent.getFocusCycleRootAncestor() ||
//                activeContainer == null || ftp == null){
//
//            //System.out.println("FocusTravel: Container diferentes");
//
//            oldComponent = null;
//            newComponent = null;
//            return ;
//        }
//
//        // flag que determina se o focus foi para baixo
//        boolean cycleDown = false;
//
//        // lista dos componentes que atravesou
//        activeFocusTraversalList = new ArrayList<FocusTraversal>();
//
//        // percorre o componente atual, que era onde o focus estava,
//        // até o componente onde o focus está,
//        // No caso do focus atual estar em um componente anterior ao componente
//        // onde o focus estava, percorre até o último camponente da janela,
//        while(!actualComponent.equals(newComponent) &&
//                !actualComponent.equals(lastComponet)){
//
//            //System.out.println("FocusTravel: " + actualComponent);
//
//            // verifica lista
//            for (FocusTraversal focusTraversal : focusTraversalList) {
//                //System.out.println("For FocusTraversal");
//                if(focusTraversal.getComponent() == actualComponent){
//                    //System.out.println("entrou: " + focusTraversal.getComponent());
//                    activeFocusTraversalList.add(focusTraversal);
//                }//else
//                    //System.out.println("NAO ENTROU: " + focusTraversal.getComponent());
//            }
//
//            // pega o proximo componente
//            try{
//                actualComponent = ftp.getComponentAfter(activeContainer,
//                        actualComponent);
//
//                // se chegar no novo componente é porque o focus foi para baixo
//                if(actualComponent.equals(newComponent))
//                    cycleDown = true;
//            }catch(Exception ex){
//                break;
//            }
//        }
//
//        for (FocusTraversal focusTraversal : activeFocusTraversalList) {
//            if(cycleDown || !focusTraversal.isOnlyWhenCycleDown()){
//                focusTraversal.getAction().actionPerformed(new ActionEvent(
//                        focusTraversal.getComponent(),
//                        ActionEvent.ACTION_PERFORMED,
//                        "focusTraversal"));
//            }
//        }
//
//        oldComponent = null;
//        newComponent = null;
//    }

    private static class FocusManagerHolder {
        private static final FocusManager INSTANCE = new FocusManager();
    }
 }
